package com.leyou.auth.service;

import com.leyou.auth.config.JwtProperties;
import com.leyou.auth.entity.ApplicationInfo;
import com.leyou.auth.mapper.ApplicationInfoMapper;
import com.leyou.common.auth.pojo.AppInfo;
import com.leyou.common.auth.pojo.Payload;
import com.leyou.common.auth.pojo.UserInfo;
import com.leyou.common.auth.utils.JwtUtils;
import com.leyou.common.enums.ExceptionEnum;
import com.leyou.common.exceptions.LyException;
import com.leyou.common.utils.CookieUtils;
import com.leyou.user.client.UserClient;
import com.leyou.user.dto.UserDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author 虎哥
 */
@Slf4j
@Service
public class AuthService {

    @Autowired
    private JwtProperties prop;

    @Autowired
    private UserClient userClient;

    public void login(String username, String password, HttpServletResponse response) {
        try {
            //- 1、授权中心携带用户名密码，到用户中心查询用户
            UserDTO user = userClient.queryUserByUsernameAndPassword(username, password);
            if (user == null) {
                throw new RuntimeException("用户名或密码不正确");
            }

            //- 2、查询如果正确，生成JWT凭证，查询错误则返回400,
            UserInfo userInfo = new UserInfo(user.getId(), user.getUsername(), "guest");
            String token = JwtUtils.generateTokenExpireInMinutes(
                    userInfo, prop.getPrivateKey(), prop.getUser().getExpireMinutes());

            //- 3、返回JWT给用户，写到cookie
            writeToken(response, token);

        } catch (Exception e) {
            throw new LyException(ExceptionEnum.INVALID_USERNAME_PASSWORD);
        }
    }

    public UserInfo verify(HttpServletRequest request, HttpServletResponse response) {
        // 1.服务端从cookie获取token
        String token = CookieUtils.getCookieValue(request, prop.getUser().getCookieName());
        try {
            // 2.解析token，获取用户信息
            Payload<UserInfo> payload = JwtUtils.getInfoFromToken(token, prop.getPublicKey(), UserInfo.class);
            // 3.验证token是否在黑名单中
            String id = payload.getId();
            Boolean hasKey = redisTemplate.hasKey(id);
            if(hasKey != null && hasKey){
                // 当前token已经被加入了黑名单，认为token无效
                throw new RuntimeException("token已经失效！");
            }

            // 3.刷新用户token
            // 3.1.获取载荷中的过期时间
            Date expiration = payload.getExpiration();
            // 3.2.获取载荷中的用户信息
            UserInfo userInfo = payload.getUserInfo();
            // 3.3.比较时间是否小于刷新的阈值
            long remainTime = expiration.getTime() - System.currentTimeMillis();
            if(remainTime < prop.getUser().getMinRefreshTime()){
                // 3.4.重新生成token
                token = JwtUtils.generateTokenExpireInMinutes(
                        userInfo, prop.getPrivateKey(), prop.getUser().getExpireMinutes());
                // 3.5.写cookie
                writeToken(response, token);
            }
            // 4.返回用户信息
            return userInfo;
        } catch (Exception e) {
            log.error("用户登录校验失败！", e);
            throw new LyException(ExceptionEnum.UNAUTHORIZED, e);
        }
    }

    private void writeToken(HttpServletResponse response, String token) {
        CookieUtils.newCookieBuilder()
                .httpOnly(true)
                .domain(prop.getUser().getDomain())
                .name(prop.getUser().getCookieName())
                .value(token)
                .response(response)
                .build();
    }

    @Autowired
    private StringRedisTemplate redisTemplate;

    public void logout(HttpServletRequest request, HttpServletResponse response) {
        // 1.获取cookie中的token
        String token = CookieUtils.getCookieValue(request, prop.getUser().getCookieName());
        // 2.验证token
        if(StringUtils.isBlank(token)){
            return;
        }
        // 3.解析token，获取过期时间，获取id
        Payload<Object> payload = JwtUtils.getInfoFromToken(token, prop.getPublicKey());
        // 4.加入黑名单，存入redis，
        // 4.1.获取过期时间
        Date expiration = payload.getExpiration();
        long remainTime = expiration.getTime() - System.currentTimeMillis();
        // 4.2.获取token的id
        String id = payload.getId();
        // 4.3.存入Redis
        redisTemplate.opsForValue().set(id, "1", remainTime, TimeUnit.MILLISECONDS);

        // 5.删除cookie
        CookieUtils.deleteCookie(prop.getUser().getCookieName(), prop.getUser().getDomain(), response);
    }

    @Autowired
    private ApplicationInfoMapper infoMapper;

    @Autowired
    private PasswordEncoder passwordEncoder;

    public String authorize(Long id, String secret) {
        // 1.去数据库根据id查询服务
        ApplicationInfo applicationInfo = infoMapper.selectByPrimaryKey(id);
        if(applicationInfo == null){
            throw new LyException(ExceptionEnum.INVALID_SERVER_ID_SECRET);
        }
        // 2.比较密码是否一致
        if(!passwordEncoder.matches(secret, applicationInfo.getSecret())){
            throw new LyException(ExceptionEnum.INVALID_SERVER_ID_SECRET);
        }
        // 3.生成JWT
        // 3.1.查询目标服务列表
        List<Long> targetList = infoMapper.queryTargetIdList(id);
        // 3.2.生成载荷信息
        AppInfo appInfo = new AppInfo();
        appInfo.setId(applicationInfo.getId());
        appInfo.setServiceName(applicationInfo.getServiceName());
        appInfo.setTargetList(targetList);
        // 3.3.生成token
        return JwtUtils.generateTokenExpireInMinutes(appInfo, prop.getPrivateKey(), prop.getApp().getExpireMinutes());
    }
}
